#include "PreCompile.hpp"
#include "Expression.hpp"
#include "NumberParsers.hpp"
#include "Tokenizer.hpp"

// see C standard:
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
// paragraph 6.5 and 6.6
// note that this implementation is not complete. It's only the stuff that's needed to 
// evaluate an expression after the #if directive

namespace CPreProc
{
  class ExpressionParser
  {
  public:
    ExpressionParser(Tokenizer &tokenizer);

    void                        do_parsing();

    const std::vector<guint32>& get_byte_code() const { return byte_code; }

  private:
    Tokenizer&                  tokenizer;
    bool                        expect_infix_operator;

    std::stack<ExpOperator>     operator_stack;
    std::vector<guint32>        byte_code;

    void                        process_punctuator(const Token &token);

    void                        process_right_parenthesis();
    void                        process_number(const Token &token);
    void                        process_prefix_operator(ExpOperator op);
    void                        process_infix_operator(ExpOperator op);
    void                        process_identifier(const Token &token);
  };

  struct OperatorPrecedence
  {
    ExpOperator op;
    int precedence;
  };
  static const OperatorPrecedence operator_precedence_list[] = 
  {
    {Op_Mul,    10},
    {Op_Div,    10},
    {Op_Mod,    10},
    {Op_Add,    9},
    {Op_Sub,    9},
    {Op_Shr,    8},
    {Op_Shl,    8},
    {Op_Lt,     7},
    {Op_Gt,     7},
    {Op_Lte,    7},
    {Op_Gte,    7},
    {Op_Eq,     6},
    {Op_Neq,    6},
    {Op_BAnd,   5},
    {Op_BXor,   4},
    {Op_BOr,    3},
    {Op_LAnd,   2},
    {Op_LOr,    1},

    // the unary operators
    {Op_Neg,    100},
    {Op_Not,    100},
    {Op_Inv,    100},

    {Op_Parenthesis,INT_MAX},
  };

  int get_operator_precedence(ExpOperator op)
  {
    const OperatorPrecedence &info = operator_precedence_list[op - Op_Mul];
    assert(info.op == op);
    return info.precedence;
  }

  ExpressionParser::ExpressionParser(Tokenizer &tokenizer)
    : tokenizer(tokenizer)
  {
  }

  void ExpressionParser::do_parsing()
  {
    expect_infix_operator = false;

    while(!tokenizer.end_of_data())
    {
      Token token = tokenizer.read_pp_token();

      switch(token.get_token_type())
      {
      case TokenType_Punctuator:
        process_punctuator(token);
        break;

      case TokenType_PPNumber:
        process_number(token);
        break;

      case TokenType_Identifier:
        process_identifier(token);
        break;

      case TokenType_WhiteSpace:
        // white space can just be skipped.
        break;



      case TokenType_Keyword:
        assert(!"not implemented yet");
        break;

      case TokenType_Constant:
        assert(!"not implemente yet");
        break;
      
      case TokenType_StringLiteral:
        assert(!"not implemente yet");
        break;
    
      case TokenType_HeaderName:
        assert(!"not implemente yet");
        break;

      

      case TokenType_CharacterConstant:
        assert(!"not implemente yet");
        break;

      

      default:
        assert(!"Unknown token");
        break;
      }
    }

      

      /*default:
        if(token.get_token_type() == TokenType_PPNumber)
        {
          
        }
        else if(token.get_token_type() == TokenType_Identifier)
        {
          
        }
        else
        {
          assert(!"not implemented yet");
        }
      }
    }*/

    while(!operator_stack.empty())
    {
      if(operator_stack.top() == Op_Parenthesis)
        throw ExpressionError("Left parenthesis not matched by a right parenthesis.");

      byte_code.push_back(operator_stack.top());
      operator_stack.pop();
    }
  }

  void ExpressionParser::process_punctuator(const Token &token)
  {
    switch(token.get_token_id())
    {
    case Punct_LeftParenthesis:
      if(expect_infix_operator)
        throw OperatorExpectedException();
      operator_stack.push(Op_Parenthesis);
      break;

    case Punct_RightParenthesis:
      process_right_parenthesis();
      break;

    case Punct_Inc:
      throw ExpressionError("The increment operator is not allowed in constant expressions.");
      break;

    case Punct_Plus:
      if(expect_infix_operator)
      {
        // it's a binary +
        process_infix_operator(Op_Add);
      }
      else
      {
        // it's a unary +, which doesn't do anything. We still call the process_prefix_operator
        // function, since it does some error checking.          
        process_prefix_operator(Op_Nop);
      }
      break;

    case Punct_Dec:
      throw ExpressionError("The decrement operator is not allowed in constant expressions.");

    case Punct_Minus:
      if(expect_infix_operator)
      {
        // it's a binary -
        process_infix_operator(Op_Sub);
      }
      else
      {
        // it's a unary -
        process_prefix_operator(Op_Neg);
      }
      break;

    case Punct_Star:
      process_infix_operator(Op_Mul);
      break;

    case Punct_Div:
      process_infix_operator(Op_Div);
      break;

    case Punct_Modulo:
      process_infix_operator(Op_Mod);
      break;

    case Punct_ShiftLeft:
      process_infix_operator(Op_Shl);
      break;

    case Punct_ShiftRight:
      process_infix_operator(Op_Shr);
      break;

    case Punct_GreaterThan:
      process_infix_operator(Op_Gt);
      break;

    case Punct_LessThan:
      process_infix_operator(Op_Lt);
      break;

    case Punct_GreaterOrEqual:
      process_infix_operator(Op_Gte);
      break;

    case Punct_LessOrEqual:
      process_infix_operator(Op_Lte);
      break;

    case Punct_Amp:
      if(expect_infix_operator)
      {
        process_infix_operator(Op_BAnd);
      }
      else
      {
        throw ExpressionError("The address-off operator is not allowed in constant expressions.");
      }
      break;

    case Punct_LogAnd:
      process_infix_operator(Op_LAnd);
      break;

    case Punct_BitOr:
      process_infix_operator(Op_BOr);
      break;

    case Punct_LogOr:
      process_infix_operator(Op_LOr);
      break;

    case Punct_BitXor:
      process_infix_operator(Op_BXor);
      break;

    case Punct_Equal:
      process_infix_operator(Op_Eq);
      break;

    case Punct_Unequal:
      process_infix_operator(Op_Neq);
      break;

    case Punct_Not:
      process_infix_operator(Op_Not);
      break;

    case Punct_Invert:
      process_infix_operator(Op_Inv);
      break;

    case Punct_Assign:
    case Punct_MulAndAssign:
    case Punct_DivAndAssign:
    case Punct_ModAndAssign:
    case Punct_AddAndAssign:
    case Punct_SubAndAssign:
    case Punct_ShiftLeftAndAssign:
    case Punct_ShiftRightAndAssign:
    case Punct_BitAndAndAssign:
    case Punct_BitXorAndAssign:
    case Punct_BitOrAndAssign:
      throw ExpressionError("Assignments are not allowed in constant expressions.");
    }
  }

  void ExpressionParser::process_right_parenthesis()
  {
    if(!expect_infix_operator)
      throw OperandExpectedException();

    while(!operator_stack.empty())
    {
      if(operator_stack.top() == Op_Parenthesis)
      {
        operator_stack.pop();
        return;
      }

      byte_code.push_back(operator_stack.top());
      operator_stack.pop();
    }

    throw ExpressionError("Right parenthesis doesn't have a matching left parenthesis.");
  }

  void ExpressionParser::process_number(const Token &token)
  {
    if(expect_infix_operator)
      throw OperatorExpectedException();

    ParsedNumber parsed_number;

    Tokenizer number_tokenizer(token.get_begin(),token.get_end());
    if(try_parse_number(number_tokenizer,parsed_number))
    {
      if(parsed_number.get_is_big_int())
      {
        assert(!"not implemented yet");
      }
      else
      {
        byte_code.push_back(Op_PushInt);
        byte_code.push_back(parsed_number.get_as_int());
      }
    }
    else
    {
      assert(!"not implemented yet");
    }

    expect_infix_operator = true;
  }

  void ExpressionParser::process_infix_operator(ExpOperator op)
  {
    if(!expect_infix_operator)
      throw OperandExpectedException();

    int precedence = get_operator_precedence(op);

    while(!operator_stack.empty())
    {
      if(operator_stack.top() == Op_Parenthesis)
      {
        break;
      }

      int stack_top_precedence = get_operator_precedence(operator_stack.top());
      if(stack_top_precedence >= precedence)
      {
        byte_code.push_back(operator_stack.top());
        operator_stack.pop();
      }
      else
        break;
    }

    operator_stack.push(op);
    expect_infix_operator = false;
  }

  void ExpressionParser::process_prefix_operator(ExpOperator op)
  {
    if(expect_infix_operator)
      throw OperatorExpectedException();

    if(op != Op_Nop)
    {
      operator_stack.push(op);
    }
  }

  // see C standard paragraph 6.4.2
  void ExpressionParser::process_identifier(const Token &token)
  {
    if(expect_infix_operator)
      throw OperatorExpectedException();

    assert(token.get_token_type() == TokenType_Identifier);
    assert(!"not implemented yet");
  }

  /////////
  // Expression
  void Expression::parse(const gchar *begin,const gchar *end)
  {
    Tokenizer tokenizer(begin,end);

    ExpressionParser parser(tokenizer);
    parser.do_parsing();
    byte_code = parser.get_byte_code();
  }

  ExpressionValue Expression::evaluate()
  {
    std::vector<ExpressionValue> stack;

    for(int i = 0;i < (int)byte_code.size();i++)
    {
      switch(byte_code[i])
      {
      case Op_PushInt:
        i++;
        if(i >= (int)byte_code.size())
          throw ExpressionError("Invalid expression byte code.");

        stack.push_back(ExpressionValue(byte_code[i]));
        break;

      case Op_PushBigInt:
        assert(!"not implemented yet");
        break;

      case Op_PushFloat:
        assert(!"not implemented yet");
        break;
      }
    }

    if(stack.size() != 1)
      throw ExpressionError("Invalid expression byte code.");

    return stack.back();
  }
}

